#include "My_INC/APP/uart_comm.h"

/**
 * @brief 将数据编码成通信帧
 *
 * 该函数将uint16_t类型的数据数组编码为自定义通信帧格式。
 * 通信帧格式：帧头(1字节) + 命令字(1字节) + 数据长度(1字节) + 数据(N*2字节) + 帧尾(1字节)
 * 帧头和帧尾使用相同的值，为传输方向标识。
 *
 * @param params 包含编码所需参数的结构体指针
 * @param frame 输出参数，用于存储编码后的帧
 * @param frame_len 输出参数，用于存储编码后帧的长度
 * @return 0表示成功，-1表示参数错误，-2表示传输方向无效
 */
int uart_encode_frame(const uart_encode_params_t *params, uint8_t *frame, uint8_t *frame_len)
{
    if (params == NULL || params->data == NULL || params->data_len > MAX_DATA_LENGTH || frame == NULL ||
        frame_len == NULL)
    {
        return -1;
    }

    // 验证传输方向是否有效
    switch (params->direction)
    {
    case FRAME_DIR_ESP32_TO_STM32:
    case FRAME_DIR_ESP32_TO_QT:
    case FRAME_DIR_STM32_TO_ESP32:
        break;
    default:
        return -2;
    }

    // 计算总帧长度：帧头(1) + 命令字(1) + 数据长度(1) + 数据(N*2) + 帧尾(1)
    uint8_t total_len = 1 + 1 + 1 + params->data_len * 2 + 1;

    uint8_t idx = 0;

    // 设置帧头（传输方向）
    frame[idx++] = params->direction;

    // 设置命令字
    frame[idx++] = params->cmd;

    // 设置数据长度（数据项数量）
    frame[idx++] = params->data_len;

    // 转换uint16_t数据为两个字节并复制
    for (int i = 0; i < params->data_len; i++)
    {
        uint16_t value = params->data[i];

        frame[idx++] = value >> 8;   // 高字节
        frame[idx++] = value & 0xFF; // 低字节
    }

    // 设置帧尾（与帧头相同，使用传输方向值）
    frame[idx++] = params->direction;

    *frame_len = idx;
    return 0;
}
/**
 * @brief 将通信帧解码成数据
 *
 * 该函数将按照自定义通信帧格式解析帧数据，提取命令字和数据内容。
 * 通信帧格式：帧头(1字节) + 命令字(1字节) + 数据长度(1字节) + 数据(N*2字节) + 帧尾(1字节)
 * 函数会校验帧头和帧尾是否一致，以及帧长度是否符合预期。
 *
 * @param frame 要解码的帧数据
 * @param frame_len 帧数据长度
 * @param decoded_frame 输出参数，用于存储解码后的数据
 * @return 0表示成功，-1表示参数错误，-2表示帧头无效，-3表示帧长度错误，-4表示帧尾校验失败
 */
int uart_decode_frame(const uint8_t *frame, const uint8_t frame_len, uart_frame_t *decoded_frame)
{
    if (frame == NULL || decoded_frame == NULL || frame_len < 4)
    {
        return -1;
    }

    // 检查帧头/传输方向
    uint8_t direction = frame[0];

    // 验证传输方向是否有效
    switch (direction)
    {
    case FRAME_DIR_ESP32_TO_STM32:
    case FRAME_DIR_ESP32_TO_QT:
    case FRAME_DIR_STM32_TO_ESP32:
        break;
    default:
        return -2;
    }

    // 获取命令字
    uint8_t cmd = frame[1];

    // 获取数据项数量
    uint8_t data_len = frame[2];

    // 验证帧长度：帧头(1) + 命令字(1) + 数据长度(1) + 数据(N*2) + 帧尾(1)
    if (frame_len != (1 + 1 + 1 + data_len * 2 + 1))
    {
        return -3;
    }

    // 检查帧尾（应该与帧头相同，使用传输方向值）
    if (frame[frame_len - 1] != direction)
    {
        return -4;
    }

    // 从字节流中还原uint16_t数据
    for (int i = 0; i < data_len; i++)
    {
        uint8_t high_byte = frame[3 + i * 2];
        uint8_t low_byte = frame[3 + i * 2 + 1];

        // 组合成uint16_t值
        uint16_t value = (high_byte << 8) | low_byte;

        decoded_frame->data[i] = value;
    }

    decoded_frame->length = data_len;
    decoded_frame->cmd = cmd;

    return 0; // 解码成功
}
